<?php declare(strict_types=1);

/**
 * @author      guosir
 * @copyright   2023-2025 owner
 **/

namespace Srv\Apps\Apis\User\Ctl;

use Srv\Apps\Apis\Common\Ctl\DevicePushCtl;
use Srv\Apps\Apis\Common\Ctl\StorageCtl;
use Srv\Apps\Apis\Common\DefCtl\DefAreaAllCtl;
use Srv\Apps\Apis\Common\DefCtl\DefBalanceCategoryCtl;
use Srv\Apps\Apis\Common\DefCtl\DefBillsBizCtl;
use Srv\Apps\Apis\Common\DefCtl\DefChannelCtl;
use Srv\Apps\Apis\Common\DefCtl\DefDeviceLangCtl;
use Srv\Apps\Apis\Common\DefCtl\DefPlatformCtl;
use Srv\Apps\Apis\Common\DefCtl\DefUserRegTypeCtl;
use Srv\Apps\Apis\Common\IdxCtl\IdxIp;
use Srv\Apps\Apis\Skit\Ctl\SkitInfoCtl;
use Srv\Apps\Apis\Skit\Ctl\SkitSeriesCtl;
use Srv\Apps\Apis\Task\Ctl\TaskCtl;
use Srv\Apps\Apis\Wallet\Ctl\WalletCtl;
use Srv\Apps\Proto\DTO\User\RechargeActRecordListDTO;
use Srv\Apps\Proto\DTO\User\UserBillListDTO;
use Srv\Apps\Proto\DTO\User\UserBindListDTO;
use Srv\Apps\Proto\DTO\User\UserFullListDTO;
use Srv\Apps\Proto\DTO\User\UserInfoEditDTO;
use Srv\Apps\Proto\DTO\User\UserOnlineListDTO;
use Srv\Apps\Proto\DTO\User\UserSkitChasingListDTO;
use Srv\Apps\Proto\DTO\User\UserSkitUnlockListDTO;
use Srv\Apps\Proto\DTO\User\UserSkitWatchListDTO;
use Srv\Apps\Proto\DTO\User\UserSkitPlayListDTO;
use Srv\Apps\Proto\DTO\User\UserTaskRewardListDTO;
use Srv\Apps\Proto\DTO\User\UserTokenListDTO;
use Srv\Apps\Proto\Enum\Skit\SeriesUnlockTypeEnum;
use Srv\Apps\Proto\Enum\Task\TaskTypeEnum;
use Srv\Apps\Proto\Enum\WalletBalanceTypeEnum;
use Srv\Apps\Proto\Enum\YesOrNoEnum;
use Srv\Apps\Proto\VO\Common\DevicePushUserVO;
use Srv\Apps\Proto\VO\Skit\SkitInfoVO;
use Srv\Apps\Proto\VO\Skit\SkitSeriesVO;
use Srv\Apps\Proto\VO\User\RechargeActRecordListVO;
use Srv\Apps\Proto\VO\User\RechargeActRecordVO;
use Srv\Apps\Proto\VO\User\UserBillListVO;
use Srv\Apps\Proto\VO\User\UserBillVO;
use Srv\Apps\Proto\VO\User\UserBindListVO;
use Srv\Apps\Proto\VO\User\UserBindVO;
use Srv\Apps\Proto\VO\User\UserFullListVO;
use Srv\Apps\Proto\VO\User\UserFullVO;
use Srv\Apps\Proto\VO\User\UserInfoBasicVO;
use Srv\Apps\Proto\VO\User\UserInfoVO;
use Srv\Apps\Proto\VO\User\UserOnlineListVO;
use Srv\Apps\Proto\VO\User\UserOnlineVO;
use Srv\Apps\Proto\VO\User\UserSkitChasingListVO;
use Srv\Apps\Proto\VO\User\UserSkitChasingVO;
use Srv\Apps\Proto\VO\User\UserSkitUnlockListVO;
use Srv\Apps\Proto\VO\User\UserSkitUnlockVO;
use Srv\Apps\Proto\VO\User\UserSkitWatchListVO;
use Srv\Apps\Proto\VO\User\UserSkitWatchVO;
use Srv\Apps\Proto\VO\User\UserSkitPlayListVO;
use Srv\Apps\Proto\VO\User\UserSkitPlayVO;
use Srv\Apps\Proto\VO\User\UserTaskRewardListVO;
use Srv\Apps\Proto\VO\User\UserTaskRewardVO;
use Srv\Apps\Proto\VO\User\UserTokenListVO;
use Srv\Apps\Proto\VO\User\UserTokenVO;
use Srv\Apps\Proto\VO\User\UserVipStatusVO;
use Srv\Apps\Proto\VO\User\UserVO;
use Srv\Apps\Proto\VO\User\UserWalletVO;
use Srv\Libs\Common\CommArray;
use Srv\Libs\Common\CommJson;
use Srv\Libs\Common\CommTime;
use Srv\Libs\Frame\AuthData;
use Srv\Libs\Frame\ControllerAbstract;
use Srv\Libs\Frame\Page;
use Srv\Libs\Storage\DataMysql;
use Srv\Libs\Tools\IpAddr;

final class UserCtl extends ControllerAbstract
{
    private ?DataMysql $DbAuto              = null;     // DB-Write

    /**
     * @return static
     * tag
     */
    final public static function tag():self
    {
        $thisObject                         = self::tagGet();
        if($thisObject instanceof self){
            $thisObject->DbAuto             = self::getDbAuto();
        }
        return $thisObject;
    }

    /**
     * @param int $userId
     * @return array
     * getUserSrc
     */
    public function getUserSrc(int $userId):array
    {
        if($userId < 1) return [];
        $isFail = false;
        $sql    = 'SELECT user_id, user_username, user_signin, durt_id, dcht_id_first, ddpt_id_first, dup_id_first, user_ip_first, dall_code_first, user_lat_first, user_lng_first, dcht_id_last, ddpt_id_last, dup_id_last, user_ip_last, dall_code_last, user_signin_time_last, user_version_last, user_lat_last, user_lng_last, ddlt_id_last, user_last_zone_offset, user_signin_num, user_src_type_first, ptlr_id_first, walr_id_first, user_time_first, user_src_type_last, ptlr_id_last, walr_id_last, user_time_last, user_status_match, user_pixel_id_match, user_chnl_match, user_type_match, ptrr_id_match, ptlr_id_match, warr_id_match, walr_id_match, user_create_time, user_update_time FROM '.$this->DbAuto->getTableNameAll('user').' WHERE user_id='.$userId.' AND user_delete=0';
        $info   = $this->DbAuto->getDataOne($sql, $isFail);
        if($isFail || !isset($info['user_id'])) return [];
        return $info;
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserSrcBatch
     */
    public function getUserSrcBatch(array $userIdList):array
    {
        if(count($userIdList) < 1) return [];
        $isFail     = false;
        $sql        = 'SELECT user_id, user_username, user_signin, durt_id, dcht_id_first, ddpt_id_first, dup_id_first, user_ip_first, dall_code_first, user_lat_first, user_lng_first, dcht_id_last, ddpt_id_last, dup_id_last, user_ip_last, dall_code_last, user_signin_time_last, user_version_last, user_lat_last, user_lng_last, ddlt_id_last, user_last_zone_offset, user_signin_num, user_src_type_first, ptlr_id_first, walr_id_first, user_time_first, user_src_type_last, ptlr_id_last, walr_id_last, user_time_last, user_status_match, user_pixel_id_match, user_chnl_match, user_type_match, ptrr_id_match, ptlr_id_match, warr_id_match, walr_id_match, user_create_time, user_update_time FROM '.$this->DbAuto->getTableNameAll('user').' WHERE user_id IN('.implode(',', $userIdList).') AND user_delete=0';
        $list       = $this->DbAuto->getData($sql, $isFail);
        $listData   = [];
        if(!$isFail && CommArray::isForeach($list)) foreach ($list as $item){
            $userIdItem             = intval($item['user_id']??0);
            if($userIdItem < 1) continue;
            $listData[$userIdItem]  = $item;
        }
        return $listData;
    }

    /**
     * @param int $userId
     * @return array
     * getUserInfoSrc
     */
    public function getUserInfoSrc(int $userId):array
    {
        if($userId < 1) return [];
        $isFail = false;
        $sql    = 'SELECT user_id, usi_idnum, usi_nickname, usi_signature, usi_avatar, usi_birthday, usi_gender, usi_cover, usi_active_time, usi_online_time, usi_online_days, usi_finish, usi_finish_time, usi_tourist, usi_tourist_time, usi_logout, usi_logout_time, usi_create_time, usi_update_time FROM '.$this->DbAuto->getTableNameAll('user_info').' WHERE user_id='.$userId.' AND usi_delete=0';
        $info   = $this->DbAuto->getDataOne($sql, $isFail);
        if($isFail || !isset($info['user_id'])) return [];
        return $info;
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserSrcBatch
     */
    public function getUserInfoSrcBatch(array $userIdList):array
    {
        if(count($userIdList) < 1) return [];
        $isFail     = false;
        $sql        = 'SELECT user_id, usi_idnum, usi_nickname, usi_signature, usi_avatar, usi_birthday, usi_gender, usi_cover, usi_active_time, usi_online_time, usi_online_days, usi_finish, usi_finish_time, usi_tourist, usi_tourist_time, usi_logout, usi_logout_time, usi_create_time, usi_update_time FROM '.$this->DbAuto->getTableNameAll('user_info').' WHERE user_id IN('.implode(',', $userIdList).') AND usi_delete=0';
        $list       = $this->DbAuto->getData($sql, $isFail);
        $listData   = [];
        if(!$isFail && CommArray::isForeach($list)) foreach ($list as $item){
            $userIdItem             = intval($item['user_id']??0);
            if($userIdItem < 1) continue;
            $listData[$userIdItem]  = $item;
        }
        return $listData;
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserVipStatusSrcBatch
     */
    public function getUserVipStatusSrcBatch(array $userIdList):array
    {
        if(count($userIdList) < 1) return [];
        $isFail     = false;
        $sql        = 'SELECT user_id, vusr_open_num, vusr_open_time, vusr_expire_time, vusr_status, vusr_create_time, vusr_update_time FROM '.$this->DbAuto->getTableNameAll('vip_status_record').' WHERE user_id IN('.implode(',', $userIdList).')';
        $list       = $this->DbAuto->getData($sql, $isFail);
        $listData   = [];
        if(!$isFail && CommArray::isForeach($list)) foreach ($list as $item){
            $userIdItem             = intval($item['user_id']??0);
            if($userIdItem < 1) continue;
            $listData[$userIdItem]  = $item;
        }
        return $listData;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserFullListVO|null
     * getUserFullList
     */
    public function getUserFullList(Page $Page, array $whereData):?UserFullListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE user_delete=0 ';
        $whereInfoString    = ' WHERE usi_delete=0';
        $infoSearch         = false;
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserFullListDTO::_userId:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    $whereInfoString    .= (strlen($whereInfoString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserFullListDTO::_username:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'user_username LIKE \'%'.$this->DbAuto->getRealEscapeString($val).'%\'';
                    break;
                case UserFullListDTO::_signIn:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'user_signin='.intval($val);
                    break;
                case UserFullListDTO::_platform:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'ddpt_id_first='.intval($val);
                    break;
                case UserFullListDTO::_platformKey:
                    $ddptId             = DefPlatformCtl::tag()->getIdByKey($val);
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'ddpt_id_first='.$ddptId;
                    break;
                case UserFullListDTO::_regType:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'durt_id='.intval($val);
                    break;
                case UserFullListDTO::_regTypeKey:
                    $durtId             = DefUserRegTypeCtl::tag()->getIdByKey($val);
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'durt_id='.$durtId;
                    break;
                case UserFullListDTO::_idNum:
                    $infoSearch         = true;
                    $whereInfoString    .= (strlen($whereInfoString)>0?' AND ':'').((strlen($val) > 10 && is_numeric($val)) ? 'user_id='.intval($val) : 'usi_idnum LIKE \'%'.$this->DbAuto->getRealEscapeString($val).'%\'');
                    break;
                case UserFullListDTO::_nickname:
                    $infoSearch         = true;
                    $whereInfoString    .= (strlen($whereInfoString)>0?' AND ':'').'usi_nickname LIKE \'%'.$this->DbAuto->getRealEscapeString($val).'%\'';
                    break;
                case UserFullListDTO::_gender:
                    $infoSearch         = true;
                    $whereInfoString    .= (strlen($whereInfoString)>0?' AND ':'').'usi_gender='.intval($val);
                    break;
                case UserFullListDTO::_finish:
                    $infoSearch         = true;
                    $whereInfoString    .= (strlen($whereInfoString)>0?' AND ':'').'usi_finish='.intval($val);
                    break;
                case UserFullListDTO::_tourist:
                    $infoSearch         = true;
                    $whereInfoString    .= (strlen($whereInfoString)>0?' AND ':'').'usi_tourist='.intval($val);
                    break;
                case UserFullListDTO::_logout:
                    $infoSearch         = true;
                    $whereInfoString    .= (strlen($whereInfoString)>0?' AND ':'').'usi_logout='.intval($val);
                    break;
                case UserFullListDTO::_isVip:
                    $currTime           = CommTime::getTimeStamp(0);
                    $tempSql            = 'SELECT user_id FROM '.$this->DbAuto->getTableNameAll('vip_status_record').' WHERE vusr_status='.YesOrNoEnum::YES_OR_NO_YES.' AND vusr_expire_time>'.$currTime;
                    if(intval($val) !== YesOrNoEnum::YES_OR_NO_YES){
                        $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id NOT IN('. $tempSql .')';
                    }else{
                        $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id IN('. $tempSql .')';
                    }
                    break;
                case UserFullListDTO::_createTimeMin:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'user_create_time>='. intval($val);
                    break;
                case UserFullListDTO::_createTimeMax:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'user_create_time<='. intval($val);
                    break;
                case UserFullListDTO::_mobile:
                    $durtId             = DefUserRegTypeCtl::tag()->getIdByKey(DefUserRegTypeCtl::KEY_REG_MOBILE);
                    $tempSql            = 'SELECT DISTINCT user_id FROM '.$this->DbAuto->getTableNameAll('user_bind_record').' WHERE usbr_delete=0 AND usbr_enable=1 AND durt_id='.$durtId.' AND usbr_openid LIKE \'%'.$this->DbAuto->getRealEscapeString($val).'%\'';
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'user_id IN('. $tempSql .')';
                    break;
                case UserFullListDTO::_channel:
                    $dchtId             = DefChannelCtl::tag()->getIdByKey($val);
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'(dcht_id_first='.$dchtId.' OR dcht_id_last='.$dchtId.')';
                    break;
                case UserFullListDTO::_channelMatch:
                    if($val === '1'){
                        $whereString    .= (strlen($whereString)>0?' AND ':'').'LENGTH(user_chnl_match)>0';
                    }else{
                        $whereString    .= (strlen($whereString)>0?' AND ':'').'user_chnl_match LIKE \''.$this->DbAuto->getRealEscapeString($val).'%\'';
                    }
                    break;
                case UserFullListDTO::_pixelIdMatch:
                    if($val === '1'){
                        $whereString    .= (strlen($whereString)>0?' AND ':'').'LENGTH(user_pixel_id_match)>0';
                    }else{
                        $whereString    .= (strlen($whereString)>0?' AND ':'').'user_pixel_id_match LIKE \''.$this->DbAuto->getRealEscapeString($val).'%\'';
                    }
                    break;
                case UserFullListDTO::_linkIdList:
                    $putIdList          = [];
                    $wappIdList         = [];
                    foreach ($val as $item){
                        if(!is_string($item)) continue;
                        $linkIdArr      = explode('_', $item);
                        $type           = intval($linkIdArr[0]??0);
                        $linkId         = intval($linkIdArr[1]??0);
                        if($type === 1){
                            if($linkId > 0) $putIdList[]    = $linkId;
                        }else if($type === 2){
                            if($linkId > 0) $wappIdList[]   = $linkId;
                        }
                    }
                    if(count($putIdList) > 0 && count($wappIdList) > 0){
                        $whereString    .= (strlen($whereString) > 0?' AND ':'').'(ptlr_id_match IN ('.implode(',', $putIdList).') OR walr_id_match IN ('.implode(',', $wappIdList).'))';
                    }else if(count($putIdList) > 0){
                        $whereString    .= (strlen($whereString) > 0?' AND ':'').'ptlr_id_match IN ('.implode(',', $putIdList).')';
                    }else if(count($wappIdList) > 0){
                        $whereString    .= (strlen($whereString) > 0?' AND ':'').'walr_id_match IN ('.implode(',', $wappIdList).')';
                    }
                    break;
            }
        }
        if($infoSearch){
            $subSql         = 'SELECT user_id FROM '.$this->DbAuto->getTableNameAll('user_info').$whereInfoString;
            $whereString    .=  (strlen($whereString)>0?' AND ':'').'user_id IN('.$subSql.')';
        }
        $isFail             = false;
        $sql                = 'SELECT SQL_CALC_FOUND_ROWS user_id, user_username, user_signin, durt_id, dcht_id_first, ddpt_id_first, dup_id_first, user_ip_first, dall_code_first, user_lat_first, user_lng_first, dcht_id_last, ddpt_id_last, dup_id_last, user_ip_last, dall_code_last, user_signin_time_last, user_version_last, user_lat_last, user_lng_last, ddlt_id_last, user_last_zone_offset, user_signin_num, user_src_type_first, ptlr_id_first, walr_id_first, user_time_first, user_src_type_last, ptlr_id_last, walr_id_last, user_time_last, user_status_match, user_pixel_id_match, user_chnl_match, user_type_match, ptrr_id_match, ptlr_id_match, warr_id_match, walr_id_match, user_create_time, user_update_time FROM '.$this->DbAuto->getTableNameAll('user').$whereString.' ORDER BY user_id DESC '.$limitString;
        $list               = $this->DbAuto->getData($sql, $isFail);
        $totalNum           = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserFullVOList     = [];
        if(CommArray::isForeach($list)){
            $userIdList             = array_map('intval', array_column($list, 'user_id'));
            $UserInfoVOList         = $this->getUserInfoBatch($userIdList);
            $UserVipStatusVOList    = $this->getUserVipStatusBatch($userIdList);
            $UserWalletVOList       = $this->getUserWalletBatch($userIdList);
            $UserBindVOListBatch    = $this->getUserBindListBatch($userIdList);
            foreach($list as $item){
                $userIdItem             = intval($item['user_id']);
                if($userIdItem < 1) continue;
                $UserVO                 = $this->getUserVO($item);
                if(!($UserVO instanceof UserVO)) continue;
                $UserInfoVO             = $UserInfoVOList[$userIdItem]??null;
                if(!($UserInfoVO instanceof UserInfoVO)) continue;
                $UserFullVO             = new UserFullVO();
                $UserFullVO->setUser($UserVO);
                $UserFullVO->setUserInfo($UserInfoVO);
                $UserVipStatusVO        = $UserVipStatusVOList[$userIdItem]??null;
                if($UserVipStatusVO instanceof UserVipStatusVO) $UserFullVO->setVipStatus($UserVipStatusVO);
                $UserWalletVO           = $UserWalletVOList[$userIdItem]??null;
                if($UserWalletVO instanceof UserWalletVO) $UserFullVO->setWallet($UserWalletVO);
                $UserBindVOList         = $UserBindVOListBatch[$userIdItem]??[];
                if(count($UserBindVOList) > 0) $UserFullVO->setBindList($UserBindVOList);
                $UserFullVOList[]       = $UserFullVO;
            }
        }
        $UserFullListVO         = new UserFullListVO();
        $UserFullListVO->setList($UserFullVOList);
        $UserFullListVO->setPage($Page->getPageVO($totalNum));
        return $UserFullListVO;
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserBatch
     */
    public function getUserBatch(array $userIdList):array
    {
        $list       = $this->getUserSrcBatch($userIdList);
        $listData   = [];
        if(CommArray::isForeach($list)) foreach ($list as $item){
            $userIdItem             = intval($item['user_id']??0);
            if($userIdItem < 1) continue;
            $UserVO                 = $this->getUserVO($item);
            if(!($UserVO instanceof UserVO)) continue;
            $listData[$userIdItem]  = $UserVO;
        }
        return $listData;
    }

    /**
     * @param int $userId
     * @return UserInfoBasicVO|null
     * getUserInfoBasicOne
     */
    public function getUserInfoBasicOne(int $userId):?UserInfoBasicVO
    {
        if($userId < 1) return null;
        $userInfoSrc        = $this->getUserInfoSrc($userId);
        return $this->getUserInfoBasicVO($userInfoSrc);
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserInfoBasicBatch
     */
    public function getUserInfoBasicBatch(array $userIdList):array
    {
        $list       = $this->getUserInfoSrcBatch($userIdList);
        $listData   = [];
        if(CommArray::isForeach($list)) {
            $userBindMobileList         = $this->getUserBindMobileBatch($userIdList);
            foreach ($list as $item){
                $userIdItem             = intval($item['user_id']??0);
                if($userIdItem < 1) continue;
                $UserInfoBasicVO        = $this->getUserInfoBasicVO($item);
                if(!($UserInfoBasicVO instanceof UserInfoBasicVO)) continue;
                $UserInfoBasicVO->setMobile($userBindMobileList[$userIdItem]??'');
                $listData[$userIdItem]  = $UserInfoBasicVO;
            }
        }
        return $listData;
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserInfoBatch
     */
    public function getUserInfoBatch(array $userIdList):array
    {
        $list       = $this->getUserInfoSrcBatch($userIdList);
        $listData   = [];
        if(CommArray::isForeach($list)) foreach ($list as $item){
            $userIdItem             = intval($item['user_id']??0);
            if($userIdItem < 1) continue;
            $UserInfoVO             = $this->getUserInfoVO($item);
            if(!($UserInfoVO instanceof UserInfoVO)) continue;
            $listData[$userIdItem]  = $UserInfoVO;
        }
        return $listData;
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserVipStatusBatch
     */
    public function getUserVipStatusBatch(array $userIdList):array
    {
        $list       = $this->getUserVipStatusSrcBatch($userIdList);
        $listData   = [];
        if(CommArray::isForeach($list)) foreach ($list as $item){
            $userIdItem             = intval($item['user_id']??0);
            if($userIdItem < 1) continue;
            $UserVipStatusVO        = $this->getUserVipStatusVO($item);
            if(!($UserVipStatusVO instanceof UserVipStatusVO)) continue;
            $listData[$userIdItem]  = $UserVipStatusVO;
        }
        return $listData;
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserWalletBatch
     */
    public function getUserWalletBatch(array $userIdList):array
    {
        if(count($userIdList) < 1) return [];
        $WalletCtl      = WalletCtl::tag();
        $coinGiveList   = $WalletCtl->getCoinGiveNumBatch($userIdList);
        $coinList       = $WalletCtl->getCoinBalanceBatch($userIdList);
        $payTotalList   = $WalletCtl->getUserPayTotal($userIdList);
        $listData       = [];
        foreach ($userIdList as $userId){
            if($userId < 1) continue;
            $UserWalletVO       = new UserWalletVO();
            $UserWalletVO->setUserId($userId);
            $UserWalletVO->setCoin($coinList[$userId]??0);
            $UserWalletVO->setCoinGive($coinGiveList[$userId]??0);
            $UserWalletVO->setPayTotal($payTotalList[$userId]??0);
            $listData[$userId]  = $UserWalletVO;
        }
        return $listData;
    }

    /**
     * @param array $user
     * @return UserVO|null
     * getUserVO
     */
    public function getUserVO(array $user):?UserVO
    {
        $userId             = intval($user['user_id']??0);
        if($userId < 1) return null;
        $UserVO             = new UserVO();
        $IdxIp              = IdxIp::tag();
        $UserVO->setUserId($userId);
        $UserVO->setUsername(trim($user['user_username']));
        $UserVO->setSignIn(intval($user['user_signin']));
        $durtId             = intval($user['durt_id']);
        if($durtId > 0){
            $regType        = DefUserRegTypeCtl::tag()->getExtStrById($durtId, DefUserRegTypeCtl::EXT_NAME);
            if(strlen($regType) < 1) $regType = DefUserRegTypeCtl::tag()->getKeyById($durtId);
            $UserVO->setRegType($regType);
        }
        $dchtIdFirst        = intval($user['dcht_id_first']);
        if($dchtIdFirst > 0){
            $DefChannelCtl  = DefChannelCtl::tag();
            $channelFirst   = $DefChannelCtl->getExtStrById($dchtIdFirst, DefChannelCtl::EXT_NAME);
            if(strlen($channelFirst) < 1) $channelFirst = $DefChannelCtl->getKeyById($dchtIdFirst);
            $UserVO->setChannelFirst($channelFirst);
        }
        $dchtIdLast         = intval($user['dcht_id_last']);
        if($dchtIdLast > 0){
            $DefChannelCtl  = DefChannelCtl::tag();
            $channelLast    = $DefChannelCtl->getExtStrById($dchtIdLast, DefChannelCtl::EXT_NAME);
            if(strlen($channelLast) < 1) $channelLast = $DefChannelCtl->getKeyById($dchtIdLast);
            $UserVO->setChannelLast($channelLast);
        }
        $dupIdFirst         = intval($user['dup_id_first']);
        $dupIdLast          = intval($user['dup_id_last']);
        $dallCodeFirst      = intval($user['dall_code_first']);
        if($dallCodeFirst > 0){
            $areaFirst      = DefAreaAllCtl::tag()->getExtStrByKey(strval($dallCodeFirst), DefAreaAllCtl::EXT_NAME);
            $UserVO->setAreaFirst($areaFirst);
        }
        $dallCodeLast       = intval($user['dall_code_last']);
        if($dallCodeLast > 0){
            $areaLast       = DefAreaAllCtl::tag()->getExtStrByKey(strval($dallCodeLast), DefAreaAllCtl::EXT_NAME);
            $UserVO->setAreaLast($areaLast);
        }
        $ddptIdFirst        = intval($user['ddpt_id_first']);
        if($ddptIdFirst > 0){
            $platformFirst  = DefPlatformCtl::tag()->getExtStrById($ddptIdFirst, DefPlatformCtl::EXT_NAME);
            if(strlen($platformFirst) < 1) $platformFirst = DefPlatformCtl::tag()->getKeyById($ddptIdFirst);
            $UserVO->setPlatformFirst($platformFirst);
        }
        $ddptIdLast         = intval($user['ddpt_id_last']);
        if($ddptIdLast > 0){
            $platformLast   = DefPlatformCtl::tag()->getExtStrById($ddptIdLast, DefPlatformCtl::EXT_NAME);
            if(strlen($platformLast) < 1) $platformLast = DefPlatformCtl::tag()->getKeyById($ddptIdLast);
            $UserVO->setPlatformLast($platformLast);
        }
        $ipStringFirst      = $IdxIp->getIpStrByIntStr($user['user_ip_first']);
        $ipAreaFirst        = IpAddr::getLocationInfo($ipStringFirst);
        $UserVO->setUserIpFirst($ipStringFirst.'('.($ipAreaFirst['country']??'').')');
        $ipStringLast       = $IdxIp->getIpStrByIntStr($user['user_ip_last']);
        $ipAreaLast         = IpAddr::getLocationInfo($ipStringLast);
        $UserVO->setUserIpLast($ipStringLast.'('.($ipAreaLast['country']??'').')');
        $UserVO->setUserLatFirst($user['user_lat_first']);
        $UserVO->setUserLngFirst($user['user_lng_first']);
        $UserVO->setUserLatLast($user['user_lat_last']);
        $UserVO->setUserLngLast($user['user_lng_last']);
        $UserVO->setSignInTimeLast(intval($user['user_signin_time_last']));
        $UserVO->setVersionLast($user['user_version_last']);
        $ddltIdLast         = intval($user['ddlt_id_last']);
        if($ddltIdLast > 0){
            $langTypeLast   = DefDeviceLangCtl::tag()->getExtStrById($ddltIdLast, DefDeviceLangCtl::EXT_NAME);
            $UserVO->setLangTypeLast($langTypeLast);
        }
        $UserVO->setSignInNum(intval($user['user_signin_num']));
        $UserVO->setCreateTime(intval($user['user_create_time']));
        $UserVO->setUpdateTime(intval($user['user_update_time']));
        $UserVO->setUserSrcTypeFirst(intval($user['user_src_type_first']));
        $UserVO->setPtlrIdFirst(intval($user['ptlr_id_first']));
        $UserVO->setWalrIdFirst(intval($user['walr_id_first']));
        $UserVO->setUserTimeFirst(intval($user['user_time_first']));
        $UserVO->setUserSrcTypeLast(intval($user['user_src_type_last']));
        $UserVO->setPtlrIdLast(intval($user['ptlr_id_last']));
        $UserVO->setWalrIdLast(intval($user['walr_id_last']));
        $UserVO->setUserTimeLast(intval($user['user_time_last']));
        $UserVO->setPixelIdMatch(trim($user['user_pixel_id_match']));
        $UserVO->setChannelMatch(trim($user['user_chnl_match']));
        $UserVO->setTypeMatch(intval($user['user_type_match']));
        $UserVO->setPtrrIdMatch(intval($user['ptrr_id_match']));
        $UserVO->setPtlrIdMatch(intval($user['ptlr_id_match']));
        $UserVO->setWarrIdMatch(intval($user['warr_id_match']));
        $UserVO->setWalrIdMatch(intval($user['walr_id_match']));
        return $UserVO;
    }

    /**
     * @param array $userInfo
     * @return UserInfoBasicVO|null
     * getUserInfoBasicVO
     */
    public function getUserInfoBasicVO(array $userInfo):?UserInfoBasicVO
    {
        $userId             = intval($userInfo['user_id']??0);
        if($userId < 1) return null;
        $UserInfoBasicVO    = new UserInfoBasicVO();
        $StorageCtl         = StorageCtl::tag();
        $UserInfoBasicVO->setUserId($userId);
        $UserInfoBasicVO->setIdNum(trim($userInfo['usi_idnum']));
        $UserInfoBasicVO->setNickname(trim($userInfo['usi_nickname']));
        $UserInfoBasicVO->setSignature(trim($userInfo['usi_signature']));
        $avatarUrl          = $StorageCtl->getStorageUrl(trim($userInfo['usi_avatar']), true);
        $UserInfoBasicVO->setAvatar($avatarUrl);
        $UserInfoBasicVO->setBirthday(intval($userInfo['usi_birthday']));
        $UserInfoBasicVO->setGender(intval($userInfo['usi_gender']));
        $coverUrl           = $StorageCtl->getStorageUrl(trim($userInfo['usi_cover']), true);
        $UserInfoBasicVO->setCover($coverUrl);
        $UserInfoBasicVO->setLogout(intval($userInfo['usi_logout']));
        return $UserInfoBasicVO;
    }

    /**
     * @param array $userInfo
     * @return UserInfoVO|null
     * getUserInfoVO
     */
    public function getUserInfoVO(array $userInfo):?UserInfoVO
    {
        $userId     = intval($userInfo['user_id']??0);
        if($userId < 1) return null;
        $UserInfoVO = new UserInfoVO();
        $StorageCtl = StorageCtl::tag();
        $UserInfoVO->setUserId($userId);
        $UserInfoVO->setIdNum(trim($userInfo['usi_idnum']));
        $UserInfoVO->setNickname(trim($userInfo['usi_nickname']));
        $UserInfoVO->setSignature(trim($userInfo['usi_signature']));
        $avatarUrl  = $StorageCtl->getStorageUrl(trim($userInfo['usi_avatar']), true);
        $UserInfoVO->setAvatar($avatarUrl);
        $UserInfoVO->setBirthday(intval($userInfo['usi_birthday']));
        $UserInfoVO->setGender(intval($userInfo['usi_gender']));
        $coverUrl   = $StorageCtl->getStorageUrl(trim($userInfo['usi_cover']), true);
        $UserInfoVO->setCover($coverUrl);
        $UserInfoVO->setActiveTime(intval($userInfo['usi_active_time']));
        $UserInfoVO->setOnlineTime(intval($userInfo['usi_online_time']));
        $UserInfoVO->setOnlineDays(intval($userInfo['usi_online_days']));
        $UserInfoVO->setFinish(intval($userInfo['usi_finish']));
        $UserInfoVO->setFinishTime(intval($userInfo['usi_finish_time']));
        $UserInfoVO->setTourist(intval($userInfo['usi_tourist']));
        $UserInfoVO->setTouristTime(intval($userInfo['usi_tourist_time']));
        $UserInfoVO->setLogout(intval($userInfo['usi_logout']));
        $UserInfoVO->setLogoutTime(intval($userInfo['usi_logout_time']));
        $UserInfoVO->setCreateTime(intval($userInfo['usi_create_time']));
        $UserInfoVO->setUpdateTime(intval($userInfo['usi_update_time']));
        return $UserInfoVO;
    }

    /**
     * @param array $vipRecord
     * @return UserVipStatusVO|null
     * getUserVipStatusVO
     */
    public function getUserVipStatusVO(array $vipRecord):?UserVipStatusVO
    {
        $userId             = intval($vipRecord['user_id']??0);
        if($userId < 1) return null;
        $UserVipStatusVO    = new UserVipStatusVO();
        $UserVipStatusVO->setUserId($userId);
        $UserVipStatusVO->setOpenNum(intval($vipRecord['vusr_open_num']));
        $UserVipStatusVO->setOpenTime(intval($vipRecord['vusr_open_time']));
        $UserVipStatusVO->setExpireTime(intval($vipRecord['vusr_expire_time']));
        $UserVipStatusVO->setStatus(intval($vipRecord['vusr_status']));
        $UserVipStatusVO->setCreateTime(intval($vipRecord['vusr_create_time']));
        $UserVipStatusVO->setUpdateTime(intval($vipRecord['vusr_update_time']));
        return $UserVipStatusVO;
    }

    /**
     * @param int $userId
     * @param array $setData
     * @return bool
     * userEdit
     */
    public function userEdit(int $userId, array $setData):bool
    {
        if($userId < 1 || count($setData) < 1) return false;
        $setUserString  = '';
        $clearToken     = false;
        foreach ($setData as $key => $val){
            switch ($key){
                case UserInfoEditDTO::_signIn:
                    if(intval($val) === YesOrNoEnum::YES_OR_NO_NO) $clearToken = true;
                    $setUserString  .= (strlen($setUserString) > 0 ? ', ' : '').'user_signin='.intval($val);
                    break;
                default:

            }
        }
        if(strlen($setUserString) < 1) return false;
        $currTime   = CommTime::getTimeStamp();
        $isFail     = false;
        $sql        = 'UPDATE '.$this->DbAuto->getTableNameAll('user').' SET '.$setUserString.', user_update_time='.$currTime.' WHERE user_id='.$userId.' AND user_delete=0';
        $resNum     = $this->DbAuto->getDataNum($sql, $isFail);
        if($isFail || $resNum < 1) return false;
        if($clearToken) $this->clearUserToken($userId);
        return true;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserBindListVO|null
     * getUserBindList
     */
    public function getUserBindList(Page $Page, array $whereData):?UserBindListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE usbr_delete=0 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserBindListDTO::_userId:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserBindListDTO::_appId:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'usbr_appid LIKE \'%'.$this->DbAuto->getRealEscapeString($val).'%\'';
                    break;
                case UserBindListDTO::_openId:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'usbr_openid LIKE \'%'.$this->DbAuto->getRealEscapeString($val).'%\'';
                    break;
                case UserBindListDTO::_unionId:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'usbr_unionid LIKE \'%'.$this->DbAuto->getRealEscapeString($val).'%\'';
                    break;
                case UserBindListDTO::_enable:
                    $whereString        .= (strlen($whereString)>0?' AND ':'').'usbr_enable='.intval($val);
                    break;
            }
        }
        $isFail             = false;
        $sql                = 'SELECT SQL_CALC_FOUND_ROWS usbr_id, user_id, durt_id, usbr_appid, usbr_openid, usbr_unionid, usbr_token, usbr_enable, usbr_use_num, usbr_create_time, usbr_update_time FROM '.$this->DbAuto->getTableNameAll('user_bind_record').$whereString.' ORDER BY usbr_id DESC '.$limitString;
        $list               = $this->DbAuto->getData($sql, $isFail);
        $totalNum           = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserBindVOList     = [];
        if(CommArray::isForeach($list)) foreach($list as $item){
            $usbrIdItem         = intval($item['usbr_id']);
            if($usbrIdItem < 1) continue;
            $UserBindVO         = $this->getUserBindVO($item);
            if(!($UserBindVO instanceof UserBindVO)) continue;
            $UserBindVOList[]   = $UserBindVO;
        }
        $UserBindListVO         = new UserBindListVO();
        $UserBindListVO->setList($UserBindVOList);
        $UserBindListVO->setPage($Page->getPageVO($totalNum));
        return $UserBindListVO;
    }

    /**
     * @param array $userIdList
     * @return array
     * getUserBindMobileBatch
     */
    public function getUserBindMobileBatch(array $userIdList):array
    {
        $listData   = [];
        if(count($userIdList) < 1) return $listData;
        $sql        = 'SELECT user_id, usbr_openid FROM '.$this->DbAuto->getTableNameAll('user_bind_record').' WHERE user_id IN ('.implode(',', $userIdList).') AND usbr_appid=\'code_mobile\' AND usbr_delete=0 AND usbr_enable=1';
        $list       = $this->DbAuto->getData($sql);
        if(CommArray::isForeach($list)) foreach ($list as $item){
            $userIdItem             = intval($item['user_id']??0);
            if($userIdItem < 1) continue;
            $mobileItem             = trim($item['usbr_openid']);
            $listData[$userIdItem]  = $mobileItem;
        }
        return $listData;
    }

    /**
     * @param array $userIdList
     * @return array
     * 批量获取用户绑定
     */
    public function getUserBindListBatch(array $userIdList):array
    {
        $listData   = [];
        if(count($userIdList) < 1) return $listData;
        $sql        = 'SELECT usbr_id, user_id, durt_id, usbr_appid, usbr_openid, usbr_unionid, usbr_token, usbr_enable, usbr_use_num, usbr_create_time, usbr_update_time FROM '.$this->DbAuto->getTableNameAll('user_bind_record').' WHERE user_id IN ('.implode(',', $userIdList).') AND usbr_delete=0 AND usbr_enable=1';
        $list       = $this->DbAuto->getData($sql);
        if(CommArray::isForeach($list)) foreach ($list as $item){
            $userId                 = intval($item['user_id']);
            if($userId < 1) continue;
            $listData[$userId][]    = $this->getUserBindVO($item);
        }
        return $listData;
    }

    /**
     * @param array $bindRecord
     * @return UserBindVO|null
     * getUserBindVO
     */
    public function getUserBindVO(array $bindRecord):?UserBindVO
    {
        $usbrId         = intval($bindRecord['usbr_id']??0);
        if($usbrId < 1) return null;
        $UserBindVO     = new UserBindVO();
        $UserBindVO->setUsbrId($usbrId);
        $UserBindVO->setUserId(intval($bindRecord['user_id']));
        $durtId         = intval($bindRecord['durt_id']);
        if($durtId > 0){
            $regType    = DefUserRegTypeCtl::tag()->getExtStrById($durtId, DefUserRegTypeCtl::EXT_NAME);
            $UserBindVO->setRegType($regType);
        }
        $UserBindVO->setAppId($bindRecord['usbr_appid']);
        $UserBindVO->setOpenId($bindRecord['usbr_openid']);
        $UserBindVO->setUnionId($bindRecord['usbr_unionid']);
        $UserBindVO->setToken($bindRecord['usbr_token']);
        $UserBindVO->setEnable($bindRecord['usbr_enable']);
        $UserBindVO->setUseNum(intval($bindRecord['usbr_use_num']));
        $UserBindVO->setCreateTime(intval($bindRecord['usbr_create_time']));
        $UserBindVO->setUpdateTime(intval($bindRecord['usbr_update_time']));
        return $UserBindVO;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserTokenListVO|null
     * getUserTokenList
     */
    public function getUserTokenList(Page $Page, array $whereData):?UserTokenListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE 1 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserTokenListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserTokenListDTO::_nonce:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'utnr_nonce='. intval($val);
                    break;
                case UserTokenListDTO::_ddptId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'ddpt_id='. intval($val);
                    break;
                case UserTokenListDTO::_uuid:
                    $subSql         = 'SELECT dup_uuid FROM '.$this->DbAuto->getTableNameAll('device_push_user').' WHERE dup_uuid=\''.$this->DbAuto->getRealEscapeString($val);
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'dup_id IN('.$subSql.')';
                    break;

            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS utnr_id, user_id, utnr_nonce, dup_id, ddpt_id, utnr_ip, utnr_expire_time, utnr_id_renew, utnr_renew_time, utnr_delete, utnr_create_time, utnr_update_time FROM '.$this->DbAuto->getTableNameAll('user_token_record').$whereString.' ORDER BY utnr_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserTokenVOList        = [];
        if(CommArray::isForeach($list)) {
            $dupIdList              = array_map('intval', array_column($list, 'dup_id'));
            $devicePushUserSrcList  = DevicePushCtl::tag()->getDevicePushUserSrcBatch($dupIdList);
            foreach($list as $item){
                $utnrIdItem         = intval($item['utnr_id']);
                if($utnrIdItem < 1) continue;
                $UserTokenVO        = $this->getUserTokenVO($item);
                if(!($UserTokenVO instanceof UserTokenVO)) continue;
                $dupIdItem          = intval($item['dup_id']);
                $UserTokenVO->setUuid($devicePushUserSrcList[$dupIdItem]['dup_uuid']??'');
                $UserTokenVOList[]  = $UserTokenVO;
            }
        }
        $UserTokenListVO        = new UserTokenListVO();
        $UserTokenListVO->setList($UserTokenVOList);
        $UserTokenListVO->setPage($Page->getPageVO($totalNum));
        return $UserTokenListVO;
    }

    /**
     * @param array $tokenRecord
     * @return UserBindVO|null
     * getUserTokenVO
     */
    public function getUserTokenVO(array $tokenRecord):?UserTokenVO
    {
        $utnrId         = intval($tokenRecord['utnr_id']??0);
        if($utnrId < 1) return null;
        $UserTokenVO    = new UserTokenVO();
        $UserTokenVO->setUtnrId($utnrId);
        $UserTokenVO->setUserId(intval($tokenRecord['user_id']));
        $UserTokenVO->setNonce(intval($tokenRecord['utnr_nonce']));
        $ddptId         = intval($tokenRecord['ddpt_id']);
        if($ddptId > 0){
            $platform   = DefPlatformCtl::tag()->getExtStrById($ddptId, DefUserRegTypeCtl::EXT_NAME);
            $UserTokenVO->setPlatform($platform);
        }
        $ipStr          = IdxIp::tag()->getIpStrByIntStr($tokenRecord['utnr_ip']);
        $ipArea         = IpAddr::getLocationInfo($ipStr);
        $UserTokenVO->setIpStr($ipStr.'('.($ipArea['country']??'').')');
        $UserTokenVO->setExpireTime(intval($tokenRecord['utnr_expire_time']));
        $UserTokenVO->setUtnrIdRenew(intval($tokenRecord['utnr_id_renew']));
        $UserTokenVO->setRenewTime(intval($tokenRecord['utnr_renew_time']));
        $UserTokenVO->setCreateTime(intval($tokenRecord['utnr_create_time']));
        $UserTokenVO->setUpdateTime(intval($tokenRecord['utnr_update_time']));
        $UserTokenVO->setIsDel(YesOrNoEnum::parseValue($tokenRecord['utnr_delete']));
        return $UserTokenVO;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserSkitUnlockListVO|null
     * getUserSkitUnlockList
     */
    public function getUserSkitUnlockList(Page $Page, array $whereData):?UserSkitUnlockListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE 1 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserSkitUnlockListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserSkitUnlockListDTO::_skioId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skio_id='. intval($val);
                    break;
                case UserSkitUnlockListDTO::_unlockType:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skud_unlock_type='. intval($val);
                    break;
            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS skud_id, user_id, skio_id, skss_ids, skud_unlock_type, skud_buy_price, skud_use_coin, skud_use_coin_give, dup_id, skud_ip, skud_create_time FROM '.$this->DbAuto->getTableNameAll('skit_unlock_record').$whereString.' ORDER BY skud_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserSkitUnlockVOList   = [];
        if(CommArray::isForeach($list)){
            $skioIdList         = array_map('intval', array_column($list, 'skio_id'));
            $SkitInfoVOList     = SkitInfoCtl::tag()->getSkitInfoBatch($skioIdList);
            foreach($list as $item){
                $skudIdItem             = intval($item['skud_id']);
                if($skudIdItem < 1) continue;
                $UserSkitUnlockVO       = $this->getUserSkitUnlockVO($item);
                if(!($UserSkitUnlockVO instanceof UserSkitUnlockVO)) continue;
                $SkitInfoVO             = $SkitInfoVOList[intval($item['skio_id'])]??null;
                if($SkitInfoVO instanceof SkitInfoVO) $UserSkitUnlockVO->setSkitInfo($SkitInfoVO);
                $UserSkitUnlockVOList[] = $UserSkitUnlockVO;
            }
        }
        $UserSkitUnlockListVO   = new UserSkitUnlockListVO();
        $UserSkitUnlockListVO->setList($UserSkitUnlockVOList);
        $UserSkitUnlockListVO->setPage($Page->getPageVO($totalNum));
        return $UserSkitUnlockListVO;
    }

    /**
     * @param array $unlockRecord
     * @return UserSkitUnlockVO|null
     * getUserSkitUnlockVO
     */
    public function getUserSkitUnlockVO(array $unlockRecord):?UserSkitUnlockVO
    {
        $skudId                 = intval($unlockRecord['skud_id']??0);
        if($skudId < 1) return null;
        $UserSkitUnlockVO       = new UserSkitUnlockVO();
        $UserSkitUnlockVO->setSkudId($skudId);
        $UserSkitUnlockVO->setUserId(intval($unlockRecord['user_id']));
        $skssIdList             = CommJson::decodeArray($unlockRecord['skss_ids']);
        if(count($skssIdList) > 0){
            $SkitSeriesVOList   = SkitSeriesCtl::tag()->getSkitSeriesInfoBatch($skssIdList);
            $UserSkitUnlockVO->setSkitSeriesList($SkitSeriesVOList);
        }
        $ipStr                  = IdxIp::tag()->getIpStrByInt(intval($unlockRecord['skud_ip']));
        $UserSkitUnlockVO->setUnlockType(SeriesUnlockTypeEnum::parseValue($unlockRecord['skud_unlock_type']));
        $UserSkitUnlockVO->setIpStr($ipStr);
        $UserSkitUnlockVO->setBuyPrice(intval($unlockRecord['skud_buy_price']));
        $UserSkitUnlockVO->setUseCoin(intval($unlockRecord['skud_use_coin']));
        $UserSkitUnlockVO->setUseCoinGive(intval($unlockRecord['skud_use_coin_give']));
        $UserSkitUnlockVO->setCreateTime(intval($unlockRecord['skud_create_time']));
        return $UserSkitUnlockVO;
    }


    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserBillListVO|null
     * getUserBillList
     */
    public function getUserBillList(Page $Page, array $whereData):?UserBillListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE 1 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserBillListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserBillListDTO::_dbtcId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'dbtc_id='. intval($val);
                    break;
                case UserBillListDTO::_dbbzId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'dbbz_id='. intval($val);
                    break;
                case UserBillListDTO::_type:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'wbsr_type='. intval($val);
                    break;
                case UserBillListDTO::_show:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'wbsr_show='. intval($val);
                    break;
            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS wbsr_id, uwbr_id, user_id, dbtc_id, wbsr_type, wbsr_balance, wbsr_ip, dup_id, wbsr_show, dbbz_id, wbsr_remark_data, wbsr_create_time FROM '.$this->DbAuto->getTableNameAll('wallet_bills_record').$whereString.' ORDER BY wbsr_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserBillVOList         = [];
        if(CommArray::isForeach($list))foreach($list as $item){
            $wbsrIdItem         = intval($item['wbsr_id']);
            if($wbsrIdItem < 1) continue;
            $UserBillVO         = $this->getUserBillVO($item);
            if(!($UserBillVO instanceof UserBillVO)) continue;
            $UserBillVOList[]   = $UserBillVO;
        }
        $UserBillListVO         = new UserBillListVO();
        $UserBillListVO->setList($UserBillVOList);
        $UserBillListVO->setPage($Page->getPageVO($totalNum));
        return $UserBillListVO;
    }

    /**
     * @param array $billRecord
     * @return UserBillVO|null
     * getUserBillVO
     */
    public function getUserBillVO(array $billRecord):?UserBillVO
    {
        $wbsrId             = intval($billRecord['wbsr_id']??0);
        if($wbsrId < 1) return null;
        $UserBillVO         = new UserBillVO();
        $UserBillVO->setWbsrId($wbsrId);
        $UserBillVO->setUserId(intval($billRecord['user_id']));
        $UserBillVO->setBalanceCategory(DefBalanceCategoryCtl::tag()->getExtStrById(intval($billRecord['dbtc_id']), DefBalanceCategoryCtl::EXT_NAME));
        $billType           = WalletBalanceTypeEnum::parseValue($billRecord['wbsr_type']);
        $UserBillVO->setType($billType);
        $balance            = intval($billRecord['wbsr_balance']);
        $UserBillVO->setBalance($balance);
        $ipStr              = IdxIp::tag()->getIpStrByInt(intval($billRecord['wbsr_ip']));
        $UserBillVO->setIpStr($ipStr);
        $UserBillVO->setShow(YesOrNoEnum::parseValue($billRecord['wbsr_show']));
        $ddbzId             = intval($billRecord['dbbz_id']);
        $UserBillVO->setBillBizName(DefBillsBizCtl::tag()->getExtStrById($ddbzId, DefBillsBizCtl::EXT_NAME));
        $remarkDataJson     = $billRecord['wbsr_remark_data'];
        $UserBillVO->setRemarkData($remarkDataJson);
        $UserBillVO->setCreateTime(intval($billRecord['wbsr_create_time']));
        $billChar           = $billType == WalletBalanceTypeEnum::WALLET_BALANCE_TYPE_IN?'+':($billType == WalletBalanceTypeEnum::WALLET_BALANCE_TYPE_OUT?'-':'');
        $remarkData         = CommJson::decodeArray($remarkDataJson);
        switch ($ddbzId){
            case DefBillsBizCtl::tag()->getIdByKey(DefBillsBizCtl::KEY_RECHARGE):
                $UserBillVO->setBillDetail($billChar.$balance.'充值看点');
                $UserBillVO->setExpireDesc('永久有效');
                break;
            case DefBillsBizCtl::tag()->getIdByKey(DefBillsBizCtl::KEY_RECHARGE_GIVE):
                $UserBillVO->setBillDetail($billChar.$balance.'赠送看点');
                $expireTime     = intval($remarkData['expireTime']??0);
                if($expireTime > 0) $UserBillVO->setExpireDesc(CommTime::getDateTime($expireTime));
                break;
            case DefBillsBizCtl::tag()->getIdByKey(DefBillsBizCtl::KEY_RECHARGE_GIVE_EXPIRE):
                $UserBillVO->setBillDetail($billChar.$balance.'赠送看点过期');
                break;
            case DefBillsBizCtl::tag()->getIdByKey(DefBillsBizCtl::KEY_COIN_USE_UNLOCK):
                $useCoin        = intval($remarkData['useCoin']??0);
                $useCoinGive    = intval($remarkData['useCoinGive']??0);
                $unlockType     = intval($remarkData['unlockType']??0);
                if($useCoin > 0 && $useCoinGive > 0){
                    $text       = $billChar.$useCoinGive.'赠送看点,'.$billChar.$useCoin.'充值看点';
                }else if($useCoin > 0){
                    $text       = $billChar.$useCoin.'充值看点';
                }else if($useCoinGive > 0){
                    $text       = $billChar.$useCoin.'赠送看点';
                }else{
                    $text       = $billChar.$balance.'看点';
                }
                $skitInfoSrc    = SkitInfoCtl::tag()->getSkitInfoSrc(intval($remarkData['skioId']??0));
                $seriesList     = SkitSeriesCtl::tag()->getSkitSeriesSrcBatch($remarkData['skssIdList']??[]);
                $seriesStr      = implode(',', array_column($seriesList, 'skss_series'));
                $UserBillVO->setBillDetail($text);
                $billDesc       = $skitInfoSrc['skio_name'].'-'.$seriesStr;
                if($unlockType === SeriesUnlockTypeEnum::SERIES_UNLOCK_TYPE_BATCH_PAY){
                    $billDesc   = '批量解锁-购买 - '.$billDesc;
                }else if($unlockType === SeriesUnlockTypeEnum::SERIES_UNLOCK_TYPE_BATCH_BALANCE){
                    $billDesc   = '批量解锁-余额 - '.$billDesc;
                }
                $UserBillVO->setBillDesc($billDesc);
                break;
            case DefBillsBizCtl::tag()->getIdByKey(DefBillsBizCtl::KEY_RECHARGE_ADMIN):
                $UserBillVO->setBillDetail($billChar.$balance.'管理员充值看点');
                break;
            case DefBillsBizCtl::tag()->getIdByKey(DefBillsBizCtl::KEY_RECHARGE_TASK):
                $taskTypeName   = TaskCtl::tag()->getTaskTypeName(intval($remarkData['taskType']??0));
                $UserBillVO->setBillDetail($billChar.$balance.'任务领奖');
                $UserBillVO->setBillDesc($taskTypeName);
                $expireTime     = intval($remarkData['expireTime']??0);
                if($expireTime > 0) $UserBillVO->setExpireDesc(CommTime::getDateTime($expireTime));
                break;
            case DefBillsBizCtl::tag()->getIdByKey(DefBillsBizCtl::KEY_REVIEW_FEEDBACK_TASK):
                $UserBillVO->setBillDetail($billChar.$balance.'反馈评价领奖');
                $scenes         = ReviewFeedbackCtl::tag()->getReviewFeedBackScenes(intval($remarkData['scenes']??0));
                $UserBillVO->setBillDesc('场景-'.$scenes.', 星级-'. intval($remarkData['starNum']??0).', 关闭-'.intval($remarkData['closeStatus']??0));
                $expireTime     = intval($remarkData['expireTime']??0);
                if($expireTime > 0) $UserBillVO->setExpireDesc(CommTime::getDateTime($expireTime));
                break;
            case DefBillsBizCtl::tag()->getIdByKey(DefBillsBizCtl::KEY_DESKTOP_REWARD):
                $UserBillVO->setBillDetail($billChar.$balance.'添加桌面奖励');
                $expireTime     = intval($remarkData['expireTime']??0);
                if($expireTime > 0) $UserBillVO->setExpireDesc(CommTime::getDateTime($expireTime));
                break;
        }
        return $UserBillVO;
    }

    /**
     * @param int $userId
     * @return UserFullVO|null
     * getUserInfoDetail
     */
    public function getUserInfoDetail(int $userId):?UserFullVO
    {
        if($userId < 1) return null;
        $userSrc                = $this->getUserSrc($userId);
        if(!isset($userSrc['user_id'])) return null;
        $UserVO                 = $this->getUserVO($userSrc);
        if(!($UserVO instanceof UserVO)) return null;
        $userInfoSrc            = $this->getUserInfoSrc($userId);
        if(!isset($userInfoSrc['user_id'])) return null;
        $UserInfoVO             = $this->getUserInfoVO($userInfoSrc);
        if(!($UserInfoVO instanceof UserInfoVO)) return null;
        $UserVipStatusVOList    = $this->getUserVipStatusBatch([$userId]);
        $UserWalletVOList       = $this->getUserWalletBatch([$userId]);
        $UserBindVOListBatch    = $this->getUserBindListBatch([$userId]);
        $UserFullVO             = new UserFullVO();
        $UserFullVO->setUser($UserVO);
        $UserFullVO->setUserInfo($UserInfoVO);
        $UserWallet             = $UserWalletVOList[$userId]??null;
        if($UserWallet instanceof UserWalletVO) $UserFullVO->setWallet($UserWallet);
        $UserVipStatusVO        = $UserVipStatusVOList[$userId]??null;
        if($UserVipStatusVO instanceof UserVipStatusVO) $UserFullVO->setVipStatus($UserVipStatusVO);
        $dupIdFirst             = intval($userSrc['dup_id_first']);
        $dupIdLast              = intval($userSrc['dup_id_last']);
        $DevicePushUserVOList = DevicePushCtl::tag()->getDevicePushUserBatch([$dupIdFirst, $dupIdLast]);
        $DevicePushUserVOFirst  = $DevicePushUserVOList[$dupIdFirst]??null;
        $DevicePushUserVOLast   = $DevicePushUserVOList[$dupIdLast]??null;
        if($DevicePushUserVOFirst instanceof DevicePushUserVO) $UserFullVO->setDeviceFirst($DevicePushUserVOFirst);
        if($DevicePushUserVOLast instanceof DevicePushUserVO) $UserFullVO->setDeviceLast($DevicePushUserVOLast);
        $UserBindVOList         = $UserBindVOListBatch[$userId]??[];
        if(count($UserBindVOList) > 0) $UserFullVO->setBindList($UserBindVOList);
        return $UserFullVO;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserSkitWatchListVO|null
     * getUserSkitWatchList
     */
    public function getUserSkitWatchList(Page $Page, array $whereData):?UserSkitWatchListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE skwr_delete=0 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserSkitWatchListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserSkitWatchListDTO::_skioId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skio_id='. intval($val);
                    break;
                case UserSkitWatchListDTO::_skssId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skss_id='. intval($val);
                    break;
            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS skwr_id, user_id, skio_id, skss_id, skwr_create_time, skwr_update_time FROM '.$this->DbAuto->getTableNameAll('skit_watch_record').$whereString.' ORDER BY skwr_update_time DESC, skwr_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserSkitWatchVOList    = [];
        if(CommArray::isForeach($list)){
            $skioIdList         = array_map('intval', array_column($list, 'skio_id'));
            $SkitInfoVOList     = SkitInfoCtl::tag()->getSkitInfoBatch($skioIdList);
            $skssIdList         = array_map('intval', array_column($list, 'skss_id'));
            $SkitSeriesVOList   = SkitSeriesCtl::tag()->getSkitSeriesInfoBatch($skssIdList);
            foreach($list as $item){
                $skwrIdItem             = intval($item['skwr_id']);
                if($skwrIdItem < 1) continue;
                $UserSkitWatchVO        = $this->getUserSkitWatchVO($item);
                if(!($UserSkitWatchVO instanceof UserSkitWatchVO)) continue;
                $SkitInfoVO             = $SkitInfoVOList[intval($item['skio_id'])]??null;
                if($SkitInfoVO instanceof SkitInfoVO) $UserSkitWatchVO->setSkitInfo($SkitInfoVO);
                $SkitSeriesVO           = $SkitSeriesVOList[intval($item['skss_id'])]??null;
                if($SkitSeriesVO instanceof SkitSeriesVO) $UserSkitWatchVO->setSkitSeries($SkitSeriesVO);
                $UserSkitWatchVOList[]  = $UserSkitWatchVO;
            }
        }
        $UserSkitWatchListVO   = new UserSkitWatchListVO();
        $UserSkitWatchListVO->setList($UserSkitWatchVOList);
        $UserSkitWatchListVO->setPage($Page->getPageVO($totalNum));
        return $UserSkitWatchListVO;
    }

    /**
     * @param array $watchRecord
     * @return UserSkitWatchVO|null
     * getUserSkitWatchVO
     */
    public function getUserSkitWatchVO(array $watchRecord):?UserSkitWatchVO
    {
        $skwrId                 = intval($watchRecord['skwr_id']??0);
        if($skwrId < 1) return null;
        $UserSkitWatchVO        = new UserSkitWatchVO();
        $UserSkitWatchVO->setSkwrId($skwrId);
        $UserSkitWatchVO->setUserId(intval($watchRecord['user_id']));
        $UserSkitWatchVO->setCreateTime(intval($watchRecord['skwr_create_time']));
        $UserSkitWatchVO->setUpdateTime(intval($watchRecord['skwr_update_time']));
        return $UserSkitWatchVO;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserTaskRewardListVO|null
     * getUserTaskRewardList
     */
    public function getUserTaskRewardList(Page $Page, array $whereData):?UserTaskRewardListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE tkrd_delete=0 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserTaskRewardListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserTaskRewardListDTO::_taskId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'task_id='. intval($val);
                    break;
                case UserTaskRewardListDTO::_type:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'tkrd_type='. intval($val);
                    break;
                case UserTaskRewardListDTO::_dayTime:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'tkrd_day_time='. intval($val);
                    break;
                case UserTaskRewardListDTO::_pullStatus:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'tkrd_pull_status='. intval($val);
                    break;
            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS tkrd_id, user_id, task_id, tkrd_type, tkrd_day_time, tkrd_calc_data, tkrd_reward_num, tkrd_reward_time, tkrd_pull_status, tkrd_pull_time, tkrd_pull_end_time ,tkrd_create_time, tkrd_update_time FROM '.$this->DbAuto->getTableNameAll('task_reward_record').$whereString.' ORDER BY tkrd_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserTaskRewardVOList   = [];
        if(CommArray::isForeach($list))foreach($list as $item){
            $tkrdIdItem             = intval($item['tkrd_id']);
            if($tkrdIdItem < 1) continue;
            $UserTaskRewardVO       = $this->getUserTaskRewardVO($item);
            if(!($UserTaskRewardVO instanceof UserTaskRewardVO)) continue;
            $UserTaskRewardVOList[] = $UserTaskRewardVO;
        }
        $UserTaskRewardListVO   = new UserTaskRewardListVO();
        $UserTaskRewardListVO->setList($UserTaskRewardVOList);
        $UserTaskRewardListVO->setPage($Page->getPageVO($totalNum));
        return $UserTaskRewardListVO;
    }

    /**
     * @param array $info
     * @return UserTaskRewardVO|null
     * getUserTaskRewardVO
     */
    public function getUserTaskRewardVO(array $info):?UserTaskRewardVO
    {
        $tkrdId                 = intval($info['tkrd_id']??0);
        if($tkrdId < 1) return null;
        $UserTaskRewardVO        = new UserTaskRewardVO();
        $UserTaskRewardVO->setTkrdId($tkrdId);
        $UserTaskRewardVO->setUserId(intval($info['user_id']));
        $UserTaskRewardVO->setTaskId(intval($info['task_id']));
        $UserTaskRewardVO->setType(TaskTypeEnum::parseValue($info['tkrd_type']));
        $UserTaskRewardVO->setDayTime(intval($info['tkrd_day_time']));
        $UserTaskRewardVO->setCalcData($info['tkrd_calc_data']);
        $UserTaskRewardVO->setRewardNum(intval($info['tkrd_reward_num']));
        $UserTaskRewardVO->setRewardTime(intval($info['tkrd_reward_time']));
        $UserTaskRewardVO->setPullStatus(intval($info['tkrd_pull_status']));
        $UserTaskRewardVO->setPullTime(intval($info['tkrd_pull_time']));
        $UserTaskRewardVO->setPullEndTime(intval($info['tkrd_pull_end_time']));
        $UserTaskRewardVO->setCreateTime(intval($info['tkrd_create_time']));
        $UserTaskRewardVO->setUpdateTime(intval($info['tkrd_update_time']));
        return $UserTaskRewardVO;
    }

    /**
     * @param int $userId
     * @return bool
     * clearUserToken
     */
    public function clearUserToken(int $userId):bool
    {
        if($userId < 1) return false;
        $currTime               = CommTime::getTimeStamp();
        $isFail                 = false;
        $tokenKeyOldList        = $this->DbAuto->getData('SELECT utnr_id, utnr_nonce, ddpt_id FROM '.$this->DbAuto->getTableNameAll('user_token_record').' WHERE user_id='.$userId.' AND utnr_delete=0 AND utnr_expire_time>='.$currTime, $isFail);
        if(!$isFail && CommArray::isForeach($tokenKeyOldList)){
            $utnrIdList         = [];
            foreach($tokenKeyOldList as $val){
                $utnrId             = intval($val['utnr_id']);
                $nonceIntVal        = intval($val['utnr_nonce']);
                if($utnrId < 1 || $nonceIntVal < 1) continue;
                $utnrIdList[]       = $utnrId;
                AuthData::delCacheApiTokenKeyData(AuthData::getTokenKeyByNonce($nonceIntVal), $userId);
            }
            if(count($utnrIdList) > 0) $this->DbAuto->query('UPDATE '.$this->DbAuto->getTableNameAll('user_token_record').' SET utnr_delete=1, utnr_update_time='.$currTime.' WHERE utnr_id IN ('.implode(',', $utnrIdList).') AND user_id='.$userId.' AND utnr_delete=0');
        }
        return true;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserSkitPlayListVO|null
     * getUserSkitPlayList
     */
    public function getUserSkitPlayList(Page $Page, array $whereData):?UserSkitPlayListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE 1 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserSkitPlayListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserSkitPlayListDTO::_skioId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skio_id='. intval($val);
                    break;
                case UserSkitPlayListDTO::_skssId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skss_id='. intval($val);
                    break;
            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS skpr_id, user_id, skio_id, skss_id, dup_id, skpr_play_finish_time, skpr_create_time FROM '.$this->DbAuto->getTableNameAll('skit_play_record').$whereString.' ORDER BY skpr_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserSkitPlayVOList     = [];
        if(CommArray::isForeach($list)){
            $skioIdList         = array_map('intval', array_column($list, 'skio_id'));
            $SkitInfoVOList     = SkitInfoCtl::tag()->getSkitInfoBatch($skioIdList);
            $skssIdList         = array_map('intval', array_column($list, 'skss_id'));
            $SkitSeriesVOList   = SkitSeriesCtl::tag()->getSkitSeriesInfoBatch($skssIdList);
            foreach($list as $item){
                $skprIdItem             = intval($item['skpr_id']);
                if($skprIdItem < 1) continue;
                $UserSkitPlayVO         = $this->getUserSkitPlayVO($item);
                if(!($UserSkitPlayVO instanceof UserSkitPlayVO)) continue;
                $SkitInfoVO             = $SkitInfoVOList[intval($item['skio_id'])]??null;
                if($SkitInfoVO instanceof SkitInfoVO) $UserSkitPlayVO->setSkitInfo($SkitInfoVO);
                $SkitSeriesVO           = $SkitSeriesVOList[intval($item['skss_id'])]??null;
                if($SkitSeriesVO instanceof SkitSeriesVO) $UserSkitPlayVO->setSkitSeries($SkitSeriesVO);
                $UserSkitPlayVOList[]   = $UserSkitPlayVO;
            }
        }
        $UserSkitPlayListVO   = new UserSkitPlayListVO();
        $UserSkitPlayListVO->setList($UserSkitPlayVOList);
        $UserSkitPlayListVO->setPage($Page->getPageVO($totalNum));
        return $UserSkitPlayListVO;
    }

    /**
     * @param array $playRecord
     * @return UserSkitPlayVO|null
     * getUserSkitPlayVO
     */
    public function getUserSkitPlayVO(array $playRecord):?UserSkitPlayVO
    {
        $skprId         = intval($playRecord['skpr_id'] ?? 0);
        if ($skprId < 1) return null;
        $UserSkitPlayVO = new UserSkitPlayVO();
        $UserSkitPlayVO->setSkprId($skprId);
        $UserSkitPlayVO->setUserId(intval($playRecord['user_id']));
        $UserSkitPlayVO->setCreateTime(intval($playRecord['skpr_create_time']));
        $UserSkitPlayVO->setPlayFinishTime(intval($playRecord['skpr_play_finish_time']));
        return $UserSkitPlayVO;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserSkitChasingListVO|null
     * getUserSkitChasingList
     */
    public function getUserSkitChasingList(Page $Page, array $whereData):?UserSkitChasingListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE 1 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserSkitChasingListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserSkitChasingListDTO::_skioId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skio_id='. intval($val);
                    break;
                case UserSkitChasingListDTO::_status:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skcr_status='. intval($val);
                    break;
                case UserSkitChasingListDTO::_hotPlay:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skcr_hot_play='. intval($val);
                    break;
                case UserSkitChasingListDTO::_operateType:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skcr_operate_type='. intval($val);
                    break;
                case UserSkitChasingListDTO::_isDelete:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'skcr_delete='. intval($val);
                    break;
            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS skcr_id, user_id, skio_id, skcr_status, skcr_series_max, skcr_operate_type, skcr_hot_play, skcr_delete, skcr_create_time, skcr_update_time FROM '.$this->DbAuto->getTableNameAll('skit_chasing_record').$whereString.' ORDER BY skcr_update_time DESC, skcr_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserSkitChasingVOList  = [];
        if(CommArray::isForeach($list)){
            $skioIdList         = array_map('intval', array_column($list, 'skio_id'));
            $SkitInfoVOList     = SkitInfoCtl::tag()->getSkitInfoBatch($skioIdList);
            foreach($list as $item){
                $skcrIdItem                 = intval($item['skcr_id']);
                if($skcrIdItem < 1) continue;
                $UserSkitChasingVO          = $this->getUserSkitChasingVO($item);
                if(!($UserSkitChasingVO instanceof UserSkitChasingVO)) continue;
                $SkitInfoVO                 = $SkitInfoVOList[intval($item['skio_id'])]??null;
                if($SkitInfoVO instanceof SkitInfoVO) $UserSkitChasingVO->setSkitInfo($SkitInfoVO);
                $UserSkitChasingVOList[]    = $UserSkitChasingVO;
            }
        }
        $UserSkitChasingListVO  = new UserSkitChasingListVO();
        $UserSkitChasingListVO->setList($UserSkitChasingVOList);
        $UserSkitChasingListVO->setPage($Page->getPageVO($totalNum));
        return $UserSkitChasingListVO;
    }

    /**
     * @param array $chasingRecord
     * @return UserSkitChasingVO|null
     * getUserSkitChasingVO
     */
    public function getUserSkitChasingVO(array $chasingRecord):?UserSkitChasingVO
    {
        $skcrId             = intval($chasingRecord['skcr_id'] ?? 0);
        if ($skcrId < 1) return null;
        $UserSkitChasingVO  = new UserSkitChasingVO();
        $UserSkitChasingVO->setSkcrId($skcrId);
        $UserSkitChasingVO->setUserId(intval($chasingRecord['user_id']));
        $UserSkitChasingVO->setStatus(YesOrNoEnum::parseValue($chasingRecord['skcr_status']));
        $UserSkitChasingVO->setSeriesMax(intval($chasingRecord['skcr_series_max']));
        $UserSkitChasingVO->setOperateType(intval($chasingRecord['skcr_operate_type']));
        $UserSkitChasingVO->setHotPlay(YesOrNoEnum::parseValue($chasingRecord['skcr_hot_play']));
        $UserSkitChasingVO->setIsDelete(YesOrNoEnum::parseValue($chasingRecord['skcr_delete']));
        $UserSkitChasingVO->setCreateTime(intval($chasingRecord['skcr_create_time']));
        $UserSkitChasingVO->setUpdateTime(intval($chasingRecord['skcr_update_time']));
        return $UserSkitChasingVO;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return UserSkitChasingListVO|null
     * getUserOnlineList
     */
    public function getUserOnlineList(Page $Page, array $whereData):?UserOnlineListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE 1 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case UserOnlineListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case UserOnlineListDTO::_udolId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'udol_id='. intval($val);
                    break;
                case UserOnlineListDTO::_isApp:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'udol_app='. intval($val);
                    break;
                case UserOnlineListDTO::_dayTimeStart:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'udol_day_time>='. intval($val);
                    break;
                case UserOnlineListDTO::_dayTimeStop:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'udol_day_time<='. intval($val);
                    break;
            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS udol_id, udol_day_time, user_id, udol_time, udol_num, udol_update_time, udol_app FROM '.$this->DbAuto->getTableNameAll('user_day_online').$whereString.' ORDER BY udol_day_time DESC, udol_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $UserOnlineVOList       = [];
        if(CommArray::isForeach($list)){
            foreach($list as $item){
                $udolIdItem             = intval($item['udol_id']);
                if($udolIdItem < 1) continue;
                $UserOnlineVO           = $this->getUserOnlineVO($item);
                if(!($UserOnlineVO instanceof UserOnlineVO)) continue;
                $UserOnlineVOList[]     = $UserOnlineVO;
            }
        }
        $UserOnlineListVO       = new UserOnlineListVO();
        $UserOnlineListVO->setList($UserOnlineVOList);
        $UserOnlineListVO->setPage($Page->getPageVO($totalNum));
        return $UserOnlineListVO;
    }

    /**
     * @param array $onlineRecord
     * @return UserOnlineVO|null
     * getUserOnlineVO
     */
    public function getUserOnlineVO(array $onlineRecord):?UserOnlineVO
    {
        $udolId             = intval($onlineRecord['udol_id'] ?? 0);
        if ($udolId < 1) return null;
        $UserOnlineVO       = new UserOnlineVO();
        $UserOnlineVO->setUdolId($udolId);
        $UserOnlineVO->setDayTime(intval($onlineRecord['udol_day_time']));
        $UserOnlineVO->setUserId(intval($onlineRecord['user_id']));
        $UserOnlineVO->setTime(intval($onlineRecord['udol_time']));
        $UserOnlineVO->setNum(intval($onlineRecord['udol_num']));
        $UserOnlineVO->setUpdateTime(intval($onlineRecord['udol_update_time']));
        $UserOnlineVO->setIsApp(YesOrNoEnum::parseValue($onlineRecord['udol_app']));
        return $UserOnlineVO;
    }

    /**
     * @param Page $Page
     * @param array $whereData
     * @return RechargeActRecordListVO|null
     * getUserRechargeActList
     */
    public function getUserRechargeActList(Page $Page, array $whereData):?RechargeActRecordListVO
    {
        $limitString        = $Page->getLimitString();
        $whereString        = ' WHERE rpr_delete=0 ';
        // 检索条件
        if(count($whereData) > 0) foreach ($whereData as $key => $val){
            switch ($key){
                case RechargeActRecordListDTO::_userId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'user_id='. intval($val);
                    break;
                case RechargeActRecordListDTO::_rprId:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'rpr_id='. intval($val);
                    break;
                case RechargeActRecordListDTO::_finish:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'rpr_finish='. intval($val);
                    break;
                case RechargeActRecordListDTO::_pgspIdAndroid:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'pgsp_id_android='. intval($val);
                    break;
                case RechargeActRecordListDTO::_pgspIdApple:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'pgsp_id_apple='. intval($val);
                    break;
                case RechargeActRecordListDTO::_pgspIdWeb:
                    $whereString    .= (strlen($whereString)>0?' AND ':'').'pgsp_id_web='. intval($val);
                    break;
            }
        }
        $isFail                 = false;
        $sql                    = 'SELECT SQL_CALC_FOUND_ROWS rpr_id, user_id, rpa_id, rpr_time, pgsp_id_android, pgsp_id_apple, pgsp_id_web, rpr_finish, porr_id, rpr_create_time, rpr_update_time FROM '.$this->DbAuto->getTableNameAll('recharge_promotion_record').$whereString.' ORDER BY rpr_id DESC '.$limitString;
        $list                   = $this->DbAuto->getData($sql, $isFail);
        $totalNum               = $this->DbAuto->getDataInt('SELECT FOUND_ROWS() as num', 'num');
        if($isFail) return null;
        $RechargeActRecordVOList            = [];
        if(CommArray::isForeach($list)){
            foreach($list as $item){
                $rprIdItem                  = intval($item['rpr_id']);
                if($rprIdItem < 1) continue;
                $RechargeActRecordVO        = $this->getUserRechargeActRecordVO($item);
                if(!($RechargeActRecordVO instanceof RechargeActRecordVO)) continue;
                $RechargeActRecordVOList[]  = $RechargeActRecordVO;
            }
        }
        $RechargeActRecordListVO            = new RechargeActRecordListVO();
        $RechargeActRecordListVO->setList($RechargeActRecordVOList);
        $RechargeActRecordListVO->setPage($Page->getPageVO($totalNum));
        return $RechargeActRecordListVO;
    }

    /**
     * @param array $info
     * @return RechargeActRecordVO|null
     * getUserRechargeActRecordVO
     */
    public function getUserRechargeActRecordVO(array $info):?RechargeActRecordVO
    {
        $rprId                  = intval($info['rpr_id'] ?? 0);
        if ($rprId < 1) return null;
        $RechargeActRecordVO    = new RechargeActRecordVO();
        $RechargeActRecordVO->setRprId($rprId);
        $RechargeActRecordVO->setUserId(intval($info['user_id']));
        $RechargeActRecordVO->setRpaId(intval($info['rpa_id']));
        $RechargeActRecordVO->setTime(intval($info['rpr_time']));
        $RechargeActRecordVO->setPgspIdAndroid(intval($info['pgsp_id_android']));
        $RechargeActRecordVO->setPgspIdApple(intval($info['pgsp_id_apple']));
        $RechargeActRecordVO->setPgspIdWeb(intval($info['pgsp_id_web']));
        $RechargeActRecordVO->setFinish(YesOrNoEnum::parseValue($info['rpr_finish']));
        $RechargeActRecordVO->setCreateTime(intval($info['rpr_create_time']));
        $RechargeActRecordVO->setUpdateTime(intval($info['rpr_update_time']));
        return $RechargeActRecordVO;
    }
}